/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <condition_variable>
#include <mutex>
#include <chrono>
#include <memory>
#include "kh_sle_ssap_client.h"
#include "sle_ssap_client_interface.h"
#include "sle_hilog.h"

#ifdef __cplusplus
extern "C" {
#endif

namespace OHOS {
namespace NearLink {
namespace SleStandard {
#define SLE_MAX_SSAP_CLIENT_CALLBACK_NUM  3

static SleSsapClient* g_ssapClients[SSAP_CLIENT_NUM_MAX] = {nullptr};
static SleSsapcCallbacks* g_ssapClientCallbacks[SLE_MAX_SSAP_CLIENT_CALLBACK_NUM] = {nullptr};
static std::mutex g_ssapcCbLock;

std::condition_variable clientVariable_;
bool clientReady_ = false;
std::mutex clientMtx_;

class SleSsapClientCallbackWapper : public SleSsapClientCallback {
public:
    SleSsapClientCallbackWapper(uint8_t clientId)
    {
        clientId_ = clientId;
    }

    void OnFindService(uint16_t connectId, const ClientFindServiceRes &result, SleErrorCode errCode) override
    {
        std::lock_guard<std::mutex> lock(g_ssapcCbLock);
        for (int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
            if (g_ssapClientCallbacks[i] != nullptr && g_ssapClientCallbacks[i]->OnSleFindStructureCb != nullptr) {
                g_ssapClientCallbacks[i]->OnSleFindStructureCb(clientId_, connectId, &result, errCode);
            }
        }
    }

    void OnFindProperty(uint16_t connectId, const ClientFindPropertyRes &result, SleErrorCode errCode) override
    {
        std::lock_guard<std::mutex> lock(g_ssapcCbLock);
        for (int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
            if (g_ssapClientCallbacks[i] != nullptr && g_ssapClientCallbacks[i]->OnSleFindPropertyCb != nullptr) {
                g_ssapClientCallbacks[i]->OnSleFindPropertyCb(clientId_, connectId, &result, errCode);
            }
        }
    }

    void OnFindComplete(uint16_t connectId, const ClientFindStructureRes &result, SleErrorCode errCode) override
    {
        SLE_LOGD("OnFindComplete In");
        clientReady_ = true;
        clientVariable_.notify_one();
    }

    void OnWriteResult(uint16_t connectId, const ClientWriteRes &result, SleErrorCode errCode) override
    {
        std::lock_guard<std::mutex> lock(g_ssapcCbLock);
        for (int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
            if (g_ssapClientCallbacks[i] != nullptr && g_ssapClientCallbacks[i]->OnSleWriteCfmCb != nullptr) {
                g_ssapClientCallbacks[i]->OnSleWriteCfmCb(clientId_, connectId, &result, errCode);
            }
        }
    }

    void OnReadResult(uint16_t connectId, const ClientHandleValue &value, SleErrorCode errCode) override
    {
        std::lock_guard<std::mutex> lock(g_ssapcCbLock);
        for (int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
            if (g_ssapClientCallbacks[i] != nullptr && g_ssapClientCallbacks[i]->OnSleReadCfmCb != nullptr) {
                g_ssapClientCallbacks[i]->OnSleReadCfmCb(clientId_, connectId, &value, errCode);
            }
        }
    }

    void OnReadByUuidComplete(uint16_t connectId, const ClientReadByUuidCmpRes &result, SleErrorCode errCode) override
    {
        SLE_LOGD("OnReadByUuidComplete In");
    }

    void OnExchangeInfo(uint16_t connectId, const SsapMtuInfo &info, SleErrorCode errCode) override
    {
        std::lock_guard<std::mutex> lock(g_ssapcCbLock);
        for (int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
            if (g_ssapClientCallbacks[i] != nullptr && g_ssapClientCallbacks[i]->OnSleExchangeInfoCb != nullptr) {
                g_ssapClientCallbacks[i]->OnSleExchangeInfoCb(clientId_, connectId, &info, errCode);
            }
        }
    }

    void OnNotification(uint16_t connectId, const ClientHandleValue &value, SleErrorCode errCode) override
    {
        std::lock_guard<std::mutex> lock(g_ssapcCbLock);
        for (int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
            if (g_ssapClientCallbacks[i] != nullptr && g_ssapClientCallbacks[i]->OnSleNotificatedCb != nullptr) {
                g_ssapClientCallbacks[i]->OnSleNotificatedCb(clientId_, connectId, &value, errCode);
            }
        }
    }

    void OnIndication(uint16_t connectId, const ClientHandleValue &value, SleErrorCode errCode) override
    {
        std::lock_guard<std::mutex> lock(g_ssapcCbLock);
        for (int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
            if (g_ssapClientCallbacks[i] != nullptr && g_ssapClientCallbacks[i]->OnSleIndicatedCb != nullptr) {
                g_ssapClientCallbacks[i]->OnSleIndicatedCb(clientId_, connectId, &value, errCode);
            }
        }
    }

private:
    uint8_t clientId_;
};

static std::shared_ptr<SleSsapClientCallbackWapper> g_ssapClientCbWapper[SSAP_CLIENT_NUM_MAX] = {nullptr};

SleErrorCode InitSleSsapcService()
{
    SLE_LOGI("InitSleSsapcService");
    return SLE_NO_ERROR;
}

SleErrorCode DeinitSleSsapcService()
{
    SLE_LOGI("DeinitSleSsapcService");
    return SLE_NO_ERROR;
}

SleErrorCode AddSspaClient(const SleUuid* appUuid, uint8_t* clientId)
{
    SLE_LOGI("AddSspaClient");
    SLE_ASSERT_AND_RETURN_RET_LOG(appUuid, SLE_ERR_INVALID_PARAM, "param client uuid error!");
    SleErrorCode ret = SLE_ERR_FAILED;
    for (uint8_t i = 0; i < SSAP_CLIENT_NUM_MAX; i++) {
        if (g_ssapClients[i] == nullptr) {
            g_ssapClientCbWapper[i] = std::make_shared<SleSsapClientCallbackWapper>(i);
            g_ssapClients[i] = new SleSsapClient(*appUuid);
            *clientId = i;
            ret = g_ssapClients[i]->RegisterClientCallback(g_ssapClientCbWapper[i]);
            SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "add ssap client failed!");
            return SLE_NO_ERROR;
        }
    }
    return ret;
}

SleErrorCode RemoveClient(uint8_t clientId)
{
    SLE_LOGI("RemoveClient");
    if (clientId >= 0 && clientId < SSAP_CLIENT_NUM_MAX) {
        if (g_ssapClients[clientId] != nullptr) {
            delete g_ssapClients[clientId];
            g_ssapClients[clientId] = nullptr;
            return SLE_NO_ERROR;
        } else {
            SLE_LOGE("server id not add error!");
            return SLE_ERR_INVALID_PARAM;
        }
    } else {
        SLE_LOGE("param client id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    return SLE_ERR_FAILED;
}

SleErrorCode FindSsapStructure(uint8_t clientId, uint16_t connectId, const ClientFindStructure *paramFind)
{
    SLE_LOGI("FindSsapStructure");
    if (clientId < 0 || clientId >= SSAP_CLIENT_NUM_MAX) {
        SLE_LOGE("param client id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(paramFind, SLE_ERR_INVALID_PARAM, "param find structure error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_ssapClients[clientId], SLE_ERR_INVALID_PARAM, "no client error!");
    SleErrorCode ret = g_ssapClients[clientId]->FindSsapStructure(connectId, *paramFind);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "find ssap structure failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SendReadReqByUuid(uint8_t clientId, uint16_t connectId, const  ClientReadReqByUUID* paramRead)
{
    SLE_LOGI("SendReadReqByUuid");
    if (clientId < 0 || clientId >= SSAP_CLIENT_NUM_MAX) {
        SLE_LOGE("param client id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(paramRead, SLE_ERR_INVALID_PARAM, "param read error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_ssapClients[clientId], SLE_ERR_INVALID_PARAM, "no client error!");
    SleErrorCode ret = g_ssapClients[clientId]->ReadRequestByUuid(connectId, *paramRead);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "read request by uuid failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SendReadReq(uint8_t clientId, uint16_t connectId, uint16_t handle, SleSsapPropertyType propertyTpye)
{
    SLE_LOGI("SendReadReq");
    if (clientId < 0 || clientId >= SSAP_CLIENT_NUM_MAX) {
        SLE_LOGE("param client id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(g_ssapClients[clientId], SLE_ERR_INVALID_PARAM, "no client error!");
    SleErrorCode ret = g_ssapClients[clientId]->ReadRequest(connectId, handle, propertyTpye);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "read request failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SendWriteReq(uint8_t clientId, uint16_t connectId, const ClientWriteParam* paramWrite)
{
#if 0
    SLE_LOGD("SendWriteReq In");
#endif
    if (clientId < 0 || clientId >= SSAP_CLIENT_NUM_MAX) {
        SLE_LOGE("param client id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(paramWrite, SLE_ERR_INVALID_PARAM, "param write error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_ssapClients[clientId], SLE_ERR_INVALID_PARAM, "no client error!");
    SleErrorCode ret = g_ssapClients[clientId]->WriteRequest(connectId, *paramWrite);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "write request failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SendWriteCmd(uint8_t clientId, uint16_t connectId, const ClientWriteParam* paramWrite)
{
#if 0
    SLE_LOGD("SendWriteCmd In");
#endif
    if (clientId < 0 || clientId >= SSAP_CLIENT_NUM_MAX) {
        SLE_LOGE("param client id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(paramWrite, SLE_ERR_INVALID_PARAM, "param write error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_ssapClients[clientId], SLE_ERR_INVALID_PARAM, "no client error!");
    SleErrorCode ret = g_ssapClients[clientId]->WriteCommand(connectId, *paramWrite);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "write command failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SendExchangeInfoReq(uint8_t clientId, uint16_t connectId, const SsapMtuInfo* paramExchange)
{
    SLE_LOGI("SendWriteCmd");
    if (clientId < 0 || clientId >= SSAP_CLIENT_NUM_MAX) {
        SLE_LOGE("param client id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(paramExchange, SLE_ERR_INVALID_PARAM, "param mtu info error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_ssapClients[clientId], SLE_ERR_INVALID_PARAM, "no client error!");
    SleErrorCode ret = g_ssapClients[clientId]->ExchangeInfo(connectId, *paramExchange);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "exchange info failed!");
    return SLE_NO_ERROR;
}

SleErrorCode RegisterSsapClientCallbacks(SleSsapcCallbacks* ssapcCallbacks)
{
    SLE_LOGI("RegisterSsapClientCallbacks");
    SLE_ASSERT_AND_RETURN_RET_LOG(ssapcCallbacks, SLE_ERR_INVALID_PARAM, "ssap client callback error!");
    std::lock_guard<std::mutex> lock(g_ssapcCbLock);
    for(int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
        SLE_ASSERT_AND_RETURN_RET_LOG(g_ssapClientCallbacks[i] != ssapcCallbacks, SLE_ERR_INVALID_PARAM, "client callback has registered!");
    }
    SleErrorCode ret = SLE_ERR_FAILED;
    for(int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
        if (g_ssapClientCallbacks[i] == nullptr) {
            g_ssapClientCallbacks[i] = ssapcCallbacks;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "register client callback failed!");
    return SLE_NO_ERROR;
}

SleErrorCode UnregisterSsapClientCallbacks(SleSsapcCallbacks* ssapcCallbacks)
{
    SLE_LOGI("UnregisterSsapClientCallbacks");
    SLE_ASSERT_AND_RETURN_RET_LOG(ssapcCallbacks, SLE_ERR_INVALID_PARAM, "ssap client callback error!");
    bool registered = false;
    std::lock_guard<std::mutex> lock(g_ssapcCbLock);
    for(int i = 0; i < SLE_MAX_SSAP_CLIENT_CALLBACK_NUM; i++) {
        if (g_ssapClientCallbacks[i] == ssapcCallbacks) {
            g_ssapClientCallbacks[i] = nullptr;
            registered = true;
            break;
        }
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(registered == true, SLE_ERR_INVALID_PARAM, "client callback has not registered!");
    return SLE_NO_ERROR;
}
} // SleStandard
} // NearLink
} // OHOS

#ifdef __cplusplus
}
#endif